package cn.com.pajx.pajx_smart_platform.exception;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;

import androidx.annotation.NonNull;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

import cn.com.pajx.pajx_smart_platform.tools.AppConstant;
import cn.com.pajx.pajx_smart_platform.tools.LogUtil;
import cn.com.pajx.pajx_smart_platform.tools.MediaStoreUtil;
import cn.com.pajx.pajx_smart_platform.tools.SharePreferencesUtil;

/**
 * Created by ruancw on 2024/9/9.
 */

public abstract class CrashException implements Thread.UncaughtExceptionHandler {

    private Context mContext;
    //允许最大日志文件的数量
    private int LIMIT_LOG_COUNT = 10;
    //简单日期格式
    private SimpleDateFormat format = null;
    //保存异常日志信息集合
    private final LinkedHashMap<String, String> crashAppLog = new LinkedHashMap<>();
    //默认放在内存卡的root路径
    private String CACHE_CRASH_LOG = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "crashLog";
    //系统默认的异常类
    private Thread.UncaughtExceptionHandler mUncaughtExceptionHandler;

    /**
     * 抽象方法，
     * 在该类初始化的时候使用
     */
    public abstract void initParams(CrashException crashAppLog);

    /**
     * 发送日志文件到服务器
     *
     * @param folder 文件路径
     * @param file   文件
     * @param log    错误日志
     */
    public abstract void sendCrashLogToServer(File folder, File file, String log);

    public void init(Context context) {
        try {
            if (context == null)
                throw new NullPointerException("Application 的Context不能为空");
            this.mContext = context;
            //获取系统默认的异常处理类
            mUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
            //在获取系统异常类之前子类可以先做一些初始化的操作
            initParams(this);
            //使用当前的类为异常处理类
            Thread.setDefaultUncaughtExceptionHandler(this);
        } catch (Exception e) {
            LogUtil.e("init - " + e.getMessage());
        }
    }


    /**
     * 此类是当应用出现异常的时候执行该方法
     *
     * @param thread    线程
     * @param throwable 异常
     */
    @Override
    public void uncaughtException(@NonNull Thread thread, @NonNull Throwable throwable) {
        try {
            if (!handlerException(throwable) && mUncaughtExceptionHandler != null) {
                //如果此异常不处理则由系统自己处理
                this.mUncaughtExceptionHandler.uncaughtException(thread, throwable);
            } else {
                //退出
                android.os.Process.killProcess(android.os.Process.myPid());
                System.exit(1);
            }
        } catch (Exception e) {
            LogUtil.e("uncaughtException - " + e.getMessage());
        }
    }

    /**
     * 用户处理异常日志
     *
     * @param throwable 异常
     * @return 布尔值
     */
    private boolean handlerException(Throwable throwable) {

        try {
            if (throwable == null)
                return false;
            new Thread(() -> {
                LogUtil.e("throwable=" + throwable.getMessage());
                Looper.prepare();
//                ToastUtil.toast("程序崩溃");
                Looper.loop();
            }).start();
            //收集应用信息
            collectCrashLogInfo(mContext);
            //将日志写入文件
            writerCrashLogToFile(throwable);
            //限制日子志文件的数量
            limitAppLogCount(LIMIT_LOG_COUNT);
        } catch (Exception e) {
            LogUtil.e("handlerException - " + e.getMessage());
        }
        return false;
    }

    /**
     * 最大文件数量
     *
     * @param limitLogCount log日志数量
     */
    private void limitAppLogCount(int limitLogCount) {

        try {
            File file = new File(CACHE_CRASH_LOG);
            if (file != null && file.isDirectory()) {
                File[] files = file.listFiles(new CrashLogFilter());
                if (files != null && files.length > 0) {
                    Arrays.sort(files, comparator);
                    if (files.length > LIMIT_LOG_COUNT) {
                        for (int i = 0; i < files.length - LIMIT_LOG_COUNT; i++) {
                            files[i].delete();
                        }
                    }
                }
            }
        } catch (Exception e) {
            LogUtil.e("limitAppLogCount - " + e.getMessage());
        }
    }


    /**
     * 日志文件按日志大小排序
     */
    private final Comparator<File> comparator = new Comparator<File>() {
        @Override
        public int compare(File l, File r) {
            return Long.compare(l.lastModified(), r.lastModified());
        }
    };

    /**
     * 过滤.log的文件
     */
    public static class CrashLogFilter implements FileFilter {
        @Override
        public boolean accept(File file) {
            return file.getName().endsWith(".log");
        }
    }


    /**
     * 写入文件中
     *
     * @param ex 异常
     */
    @SuppressLint("SimpleDateFormat")
    private void writerCrashLogToFile(Throwable ex) {
        try {
            StringBuilder buffer = new StringBuilder();
            if (crashAppLog != null && !crashAppLog.isEmpty()) {
                for (Map.Entry<String, String> entry : crashAppLog.entrySet()) {
                    buffer.append(entry.getKey()).append(":").append(entry.getValue()).append("\n");
                }
            }
            Writer writer = new StringWriter();
            PrintWriter printWriter = new PrintWriter(writer);
            ex.printStackTrace(printWriter);
            Throwable cause = ex.getCause();
            while (cause != null) {
                cause.printStackTrace(printWriter);
                cause = cause.getCause();
            }
            printWriter.flush();
            printWriter.close();
            if (format == null) {
                format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
            }
            String timer = format.format(new Date());
            String result = writer.toString();
            buffer.append("Exception:").append(timer).append("\n");
            buffer.append(result);
            //上传到服务器
//            sendCrashLogToServer(null, null, buffer.toString());
            //写入本地文件
            writerToFile(buffer.toString());
            LogUtil.e("异常" + buffer);
            SharePreferencesUtil.getInstance().putString(AppConstant.CRASH_LOG, buffer.toString());
        } catch (Exception e) {
            LogUtil.e("writerCrashLogToFile --> " + e.getMessage());
        }
    }

    @SuppressLint("SimpleDateFormat")
    private void writerToFile(String log) {
        try {
            //创建日志文件名称
            String curtTimer = "" + System.currentTimeMillis();
            if (format == null) {
                format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
            }
            String timer = format.format(new Date());
            String fileName = "crash-" + timer + "-" + curtTimer + ".txt";
            //创建文件夹
            File mediaStorageDir = new File(MediaStoreUtil.getBaseMediaStore(mContext) + File.separator + "/crashLog");
            if (!mediaStorageDir.exists()) {
                mediaStorageDir.mkdirs();
            }
            File mediaFile = new File(mediaStorageDir.getAbsolutePath() + File.separator + fileName);
            FileWriter fileWriter = new FileWriter(mediaFile);
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);

            bufferedWriter.write(log);
            bufferedWriter.flush();
            bufferedWriter.close();
            LogUtil.e("日志文件夹:" + mediaFile.getAbsolutePath());
        } catch (Exception e) {
            LogUtil.e("writerToFile - " + e.getMessage());
        }
    }

    /**
     * 获取应用信息
     *
     * @param mContext context
     */
    private void collectCrashLogInfo(Context mContext) {
        try {
            if (mContext == null)
                return;
            PackageManager packageManager = mContext.getPackageManager();
            if (packageManager != null) {
                PackageInfo packageInfo = packageManager.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
                if (packageInfo != null) {
                    String versionName = packageInfo.versionName;
                    String versionCode = "" + packageInfo.versionCode;
                    String packName = packageInfo.packageName;
                    crashAppLog.put("versionName", versionName);
                    crashAppLog.put("versionCode", versionCode);
                    crashAppLog.put("packName", packName);
                }
            }
            //获取手机型号，系统版本，以及SDK版本
            crashAppLog.put("机型型号", Build.MODEL);
            crashAppLog.put("SDK版本", Build.VERSION.SDK);
            crashAppLog.put("Android版本", Build.VERSION.RELEASE);

            Field[] fields = Build.class.getFields();
            if (fields != null) {
                for (Field field : fields) {
                    if (field != null) {
                        field.setAccessible(true);
                        crashAppLog.put(field.getName(), field.get(null).toString());
                    }
                }
            }
        } catch (Exception e) {
            LogUtil.e("collectDeviceInfo - " + e.getMessage());
        }
    }


    public int getLogLimitCount() {
        return LIMIT_LOG_COUNT;
    }

    public void setLogLimitCount(int LIMIT_LOG_COUNT) {
        this.LIMIT_LOG_COUNT = LIMIT_LOG_COUNT;
    }

    public String getCrashLog() {
        return CACHE_CRASH_LOG;
    }

    public void setCrashLog(String CACHE_CRASH_LOG) {
        this.CACHE_CRASH_LOG = CACHE_CRASH_LOG;
    }

}
